package com.donger.auth;

import com.donger.auth.component.BootAuthenticationKeyGenerator;
import com.donger.auth.component.BootUserDetailsService;
import com.donger.auth.component.BootWebResponseExceptionTranslator;
import com.donger.auth.component.UserDetail;
import com.donger.common.core.constant.SecurityConstants;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证服务器配置 页脚授权服务器
 *
 * @author xyx
 */
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final DataSource dataSource;
    private final AuthenticationManager authenticationManager;
    private final BootUserDetailsService userDetailsService;
    private final RedisConnectionFactory redisConnectionFactory;

    /**
     * 修改成jdbc 模式
     *
     * @param clients
     * @throws Exception
     */

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
        clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // 允许/oauth/token 请求的方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                // 设置token 存储形式
                .tokenStore(tokenStore())
                // 自定义 一些 token 登录以后返回的结果
                .tokenEnhancer(tokenEnhancer())
                // 设置 获取用户信息的具体 实现  默认为 UserDetailsService
                .userDetailsService(userDetailsService)
                // 设置认证管理接口
                .authenticationManager(authenticationManager)
                // 设置是否重复是有 刷新token 默认为true  这里设置为false
                .reuseRefreshTokens(false)
                // 设置异常 转化
                .exceptionTranslator(new BootWebResponseExceptionTranslator());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                // 允许表单提交
                .allowFormAuthenticationForClients();
    }

    /**
     * 设置token 存储地点 和存储形式，所有的信息存储以 boot_oauth:  开头
     *
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        tokenStore.setPrefix(SecurityConstants.BOOT_PREFIX + SecurityConstants.OAUTH_PREEIX);
        tokenStore.setAuthenticationKeyGenerator(new BootAuthenticationKeyGenerator());
        return tokenStore;
    }

    /**
     * token 增强模式
     *
     * @return
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            if (SecurityConstants.CLIENT_CREDENTIALS
                    .equals(authentication.getOAuth2Request().getGrantType())) {
                return accessToken;
            }

            final Map<String, Object> additionalInfo = new HashMap<>(8);
            UserDetail userDetail = (UserDetail) authentication.getUserAuthentication().getPrincipal();
            additionalInfo.put(SecurityConstants.DETAILS_USER_ID, userDetail.getId());
            additionalInfo.put(SecurityConstants.DETAILS_USERNAME, userDetail.getUsername());
            additionalInfo.put(SecurityConstants.DETAILS_DEPT_ID, userDetail.getDeptId());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        };
    }

    /**
     * 设置密码验证时是的密码加密方式
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
